package utils

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"strconv"
	"time"
	"unsafe"
)

var (
	c_EMPTY_STRING       string
	c_BOOL_DEFAULT       bool
	c_BYTE_DEFAULT       byte
	c_COMPLEX64_DEFAULT  complex64
	c_COMPLEX128_DEFAULT complex128
	c_FLOAT32_DEFAULT    float32
	c_FLOAT64_DEFAULT    float64
	c_INT64_DEFAULT      int64
	c_UINT64_DEFAULT     uint64
	c_INT32_DEFAULT      int32
	c_UINT32_DEFAULT     uint32
	c_INT16_DEFAULT      int16
	c_UINT16_DEFAULT     uint16
	c_INT8_DEFAULT       int8
	c_UINT8_DEFAULT      uint8
	c_INT_DEFAULT        int
	c_UINT_DEFAULT       uint
	c_TIME_DEFAULT       time.Time

	s_INT_DEFAULT    []int
	s_STRING_DEFAULT []string
)

var (
	IntType   = reflect.TypeOf(c_INT_DEFAULT)
	Int8Type  = reflect.TypeOf(c_INT8_DEFAULT)
	Int16Type = reflect.TypeOf(c_INT16_DEFAULT)
	Int32Type = reflect.TypeOf(c_INT32_DEFAULT)
	Int64Type = reflect.TypeOf(c_INT64_DEFAULT)

	UintType   = reflect.TypeOf(c_UINT_DEFAULT)
	Uint8Type  = reflect.TypeOf(c_UINT8_DEFAULT)
	Uint16Type = reflect.TypeOf(c_UINT16_DEFAULT)
	Uint32Type = reflect.TypeOf(c_UINT32_DEFAULT)
	Uint64Type = reflect.TypeOf(c_UINT64_DEFAULT)

	Float32Type = reflect.TypeOf(c_FLOAT32_DEFAULT)
	Float64Type = reflect.TypeOf(c_FLOAT64_DEFAULT)

	Complex64Type  = reflect.TypeOf(c_COMPLEX64_DEFAULT)
	Complex128Type = reflect.TypeOf(c_COMPLEX128_DEFAULT)

	StringType = reflect.TypeOf(c_EMPTY_STRING)
	BoolType   = reflect.TypeOf(c_BOOL_DEFAULT)
	ByteType   = reflect.TypeOf(c_BYTE_DEFAULT)
	BytesType  = reflect.SliceOf(ByteType)

	TimeType = reflect.TypeOf(c_TIME_DEFAULT)

	IntSliceType    = reflect.TypeOf(s_INT_DEFAULT)
	StringSliceType = reflect.TypeOf(s_STRING_DEFAULT)
)

func Int32(v int32) *int32 {
	return &v
}

func ToByte(object interface{}) byte {
	if object == nil {
		return byte(0)
	}
	switch v := object.(type) {
	case byte:
		return v
	case []byte:
		return v[0]
	case int:
		return byte(v)
	case string:
		i, _ := strconv.Atoi(v)
		return byte(i)
	}
	v := To(reflect.ValueOf(object), ByteType)
	return v.Interface().(byte)
}

func ToBytes(object interface{}) []byte {
	if object == nil {
		return []byte("")
	}
	switch v := object.(type) {
	case []byte:
		//fmt.Println("[]byte", object, v)
		return v
	case string:
		//c := str2bytes(v)
		//fmt.Println(object, v, c, bytes2str(c))
		//debug.PrintStack()
		//fmt.Println("string", object, v)
		return str2bytes(v)
	case uint64:
		//fmt.Println("uint64", object, v)
		return uint642bytes(v)
	case uint16:
		//fmt.Println("uint16", object, v)
		return uint162bytes(v)
	case int64:
		//fmt.Println("int64", object, v)
		return int642bytes(v)
	case int16:
		//fmt.Println("int32", object, v)
		return int162bytes(v)
	case float32:
		//fmt.Println("float32", object, v)
		return float322bytes(v)
	case []int:
		//fmt.Println("[]int", object, v)
		ss := make([]byte, len(v))
		for key, node := range v {
			ss[key] = ToByte(node)
		}
		return ss
	}
	v := To(reflect.ValueOf(object), BytesType)
	//fmt.Println(object, v)
	return v.Interface().([]byte)
}

func ToUInt16(object interface{}) uint16 {
	if object == nil {
		return uint16(0)
	}
	switch v := object.(type) {
	case uint16:
		return v
	case int8:
		return uint16(v)
	case int16:
		return uint16(v)
	case int32:
		return uint16(v)
	case uint:
		return uint16(v)
	case uint8:
		return uint16(v)
	case int:
		return uint16(v)
	case uint32:
		return uint16(v)
	case uint64:
		return uint16(v)
	case string:
		i, _ := strconv.Atoi(v)
		return uint16(i)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return uint16(v[0])
		} else if l == 2 {
			return uint16(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return uint16(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return uint16(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Int16Type)
	return v.Interface().(uint16)
}

func ToInt16(object interface{}) int16 {
	if object == nil {
		return int16(0)
	}
	switch v := object.(type) {
	case int:
		return int16(v)
	case int8:
		return int16(v)
	case int16:
		return int16(v)
	case int32:
		return int16(v)
	case uint:
		return int16(v)
	case uint8:
		return int16(v)
	case uint16:
		return int16(v)
	case uint32:
		return int16(v)
	case uint64:
		return int16(v)
	case float64:
		return int16(v)
	case float32:
		return int16(v)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return int16(v[0])
		} else if l <= 2 {
			return int16(binary.BigEndian.Uint16(v))
		} else if l <= 4 {
			return int16(binary.BigEndian.Uint32(v))
		} else if l <= 8 {
			return int16(binary.BigEndian.Uint64(v))
		}
	case string:
		i, _ := strconv.ParseInt(v, 10, 64)
		return int16(i)
	}
	v := To(reflect.ValueOf(object), IntType)
	return v.Interface().(int16)
}
func ToInt(object interface{}) int {
	if object == nil {
		return int(0)
	}
	switch v := object.(type) {
	case int:
		return v
	case int8:
		return int(v)
	case int16:
		return int(v)
	case int32:
		return int(v)
	case uint:
		return int(v)
	case uint8:
		return int(v)
	case uint16:
		return int(v)
	case uint32:
		return int(v)
	case uint64:
		return int(v)
	case float64:
		return int(v)
	case float32:
		return int(v)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return int(v[0])
		} else if l <= 2 {
			return int(binary.BigEndian.Uint16(v))
		} else if l <= 4 {
			return int(binary.BigEndian.Uint32(v))
		} else if l <= 8 {
			return int(binary.BigEndian.Uint64(v))
		}
	case string:
		i, _ := strconv.Atoi(v)
		return i
	}
	v := To(reflect.ValueOf(object), IntType)
	return v.Interface().(int)
}

func ToInt32(object interface{}) int32 {
	if object == nil {
		return int32(0)
	}
	switch v := object.(type) {
	case int:
		return int32(v)
	case int8:
		return int32(v)
	case int16:
		return int32(v)
	case int32:
		return int32(v)
	case uint:
		return int32(v)
	case uint8:
		return int32(v)
	case uint16:
		return int32(v)
	case uint32:
		return int32(v)
	case uint64:
		return int32(v)
	case string:
		i, _ := strconv.Atoi(v)
		return int32(i)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return int32(v[0])
		} else if l == 2 {
			return int32(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return int32(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return int32(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Int32Type)
	return v.Interface().(int32)
}
func ToInt64(object interface{}) int64 {
	if object == nil {
		return int64(0)
	}
	switch v := object.(type) {
	case int64:
		return v
	case uint:
		return int64(v)
	case uint8:
		return int64(v)
	case uint16:
		return int64(v)
	case uint32:
		return int64(v)
	case int:
		return int64(v)
	case int8:
		return int64(v)
	case int16:
		return int64(v)
	case int32:
		return int64(v)
	//case []byte:
	//	return bytes2int64(v)
	case string:
		i, _ := strconv.ParseInt(v, 10, 64)
		return i
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return int64(v[0])
		} else if l == 2 {
			return int64(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return int64(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return int64(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Int64Type)
	return v.Interface().(int64)
}

func ToUint(object interface{}) uint {
	if object == nil {
		return uint(0)
	}
	switch v := object.(type) {
	case uint:
		return v
	case uint8:
		return uint(v)
	case uint16:
		return uint(v)
	case uint32:
		return uint(v)
	case uint64:
		return uint(v)
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return uint(i)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return uint(v[0])
		} else if l == 2 {
			return uint(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return uint(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return uint(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), UintType)
	return v.Interface().(uint)
}

func ToUint8(object interface{}) uint8 {
	if object == nil {
		return uint8(0)
	}
	switch v := object.(type) {
	case uint8:
		return v
	case uint:
		return uint8(v)
	case uint16:
		return uint8(v)
	case uint32:
		return uint8(v)
	case uint64:
		return uint8(v)
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return uint8(i)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return uint8(v[0])
		} else if l == 2 {
			return uint8(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return uint8(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return uint8(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Uint8Type)
	return v.Interface().(uint8)
}

func ToUint16(object interface{}) uint16 {
	if object == nil {
		return uint16(0)
	}
	switch v := object.(type) {
	case uint8:
		return uint16(v)
	case uint:
		return uint16(v)
	case uint16:
		return uint16(v)
	case uint32:
		return uint16(v)
	case uint64:
		return uint16(v)
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return uint16(i)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return uint16(v[0])
		} else if l == 2 {
			return uint16(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return uint16(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return uint16(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Uint16Type)
	return v.Interface().(uint16)
}

func ToUint32(object interface{}) uint32 {
	if object == nil {
		return uint32(0)
	}
	switch v := object.(type) {
	case uint8:
		return uint32(v)
	case uint:
		return uint32(v)
	case uint16:
		return uint32(v)
	case uint32:
		return v
	case uint64:
		return uint32(v)
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return uint32(i)
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return uint32(v[0])
		} else if l == 2 {
			return uint32(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return uint32(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return uint32(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Uint32Type)
	return v.Interface().(uint32)
}
func ToUint64(object interface{}) uint64 {
	if object == nil {
		return uint64(0)
	}
	switch v := object.(type) {
	case uint8:
		return uint64(v)
	case uint:
		return uint64(v)
	case uint16:
		return uint64(v)
	case uint32:
		return uint64(v)
	case uint64:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return i
	case bool:
		if v {
			return 1
		} else {
			return 0
		}
	case []uint8:
		l := len(v)
		if l == 1 {
			return uint64(v[0])
		} else if l == 2 {
			return uint64(binary.BigEndian.Uint16(v))
		} else if l == 4 {
			return uint64(binary.BigEndian.Uint32(v))
		} else if l == 8 {
			return uint64(binary.BigEndian.Uint64(v))
		}
	}
	v := To(reflect.ValueOf(object), Uint64Type)
	return v.Interface().(uint64)
}

func ToFloat32(object interface{}) float32 {
	if object == nil {
		return float32(0)
	}
	switch v := object.(type) {
	case float32:
		return v
	case uint:
		return float32(v)
	case uint8:
		return float32(v)
	case uint16:
		return float32(v)
	case uint32:
		return float32(v)
	case []byte:
		return bytes2float32(v)
	}
	v := To(reflect.ValueOf(object), Float32Type)
	return v.Interface().(float32)
}

func ToString(object interface{}) string {
	if object == nil {
		return ""
	}
	switch v := object.(type) {
	case string:
		return v
	case []byte:
		return bytes2str(v)
	case int:
		return strconv.Itoa(v)
	case int16:
		return strconv.Itoa(int(v))
	case int32:
		return strconv.Itoa(int(v))
	case int64:
		return strconv.FormatInt(v, 10)
	case uint16:
		return strconv.FormatUint(uint64(v), 10)
	case uint32:
		return strconv.FormatUint(uint64(v), 10)
	case uint64:
		return strconv.FormatUint(v, 10)
	case byte:
		return strconv.Itoa(int(v))
	case float32:
		return strconv.Itoa(int(v))
	case float64:
		return strconv.FormatInt(int64(v), 10)
	}
	v := To(reflect.ValueOf(object), StringType)
	return v.Interface().(string)
}

func ToBool(object interface{}) bool {
	if object == nil {
		return false
	}
	switch v := object.(type) {
	case bool:
		return object.(bool)
	case uint8:
		if v > 0 {
			return true
		} else {
			return false
		}
	case uint:
		if v > 0 {
			return true
		} else {
			return false
		}
	case int:
		if v > 0 {
			return true
		} else {
			return false
		}
	case int64:
		if v > 0 {
			return true
		} else {
			return false
		}
	case string:
		if v == "false" {
			return false
		}
		if v == "" {
			return false
		}
		if v == "0" {
			return false
		}
		return true
	}
	v := To(reflect.ValueOf(object), BoolType)
	return v.Interface().(bool)
}

func To(value reflect.Value, p reflect.Type) reflect.Value {
	if p != value.Type() {
		return value.Convert(p)
	} else {
		return value
	}
}

func AutoTo(value reflect.Value, p reflect.Type) reflect.Value {
	switch p {
	case StringType:
		if value.CanInterface() {
			v := ToString(value.Interface())
			if value.CanSet() {
				value.SetString(v)
			} else {
				return reflect.ValueOf(v)
			}
		} else {
			value.SetString(ToString(value.Convert(BytesType).Bytes()))
		}
		return value
	}
	return To(value, p)
}

func ToInterfaceSlice(object interface{}) []interface{} {
	if object == nil {
		return nil
	}
	var ss []interface{}
	switch v := object.(type) {
	case []string:
		ss = make([]interface{}, len(v))
		for key, node := range v {
			ss[key] = node
		}
		return ss
	case [][]byte:
		ss = make([]interface{}, len(v))
		for key, node := range v {
			ss[key] = node
		}
		return ss
	case []int:
		ss = make([]interface{}, len(v))
		for key, node := range v {
			ss[key] = node
		}
		return ss
	case []interface{}:
		return v
	}
	return nil
}

func ToStringSlice(object interface{}) []string {
	if object == nil {
		return nil
	}
	var ss []string
	switch v := object.(type) {
	case []string:
		return v
	case []interface{}:
		ss = make([]string, len(v))
		for key, node := range v {
			ss[key] = ToString(node)
		}
		return ss
	}
	return nil
}

func ToStringSliceSlice(object interface{}) [][]string {
	if object == nil {
		return nil
	}
	var ss [][]string
	switch v := object.(type) {
	case [][]string:
		return v
	case []interface{}:
		ss = make([][]string, len(v))
		for key, node := range v {
			ss[key] = ToStringSlice(node)
		}
		return ss
	}
	return nil
}

func ToIntSlice(object interface{}) []int {
	if object == nil {
		return nil
	}
	var ss []int
	switch v := object.(type) {
	case []int:
		return v
	case []byte:
		ss = make([]int, len(v))
		for key, node := range v {
			ss[key] = ToInt(node)
		}
		return ss
	case []string:
		ss = make([]int, len(v))
		for key, node := range v {
			ss[key] = ToInt(node)
		}
		return ss
	case []interface{}:
		ss = make([]int, len(v))
		for key, node := range v {
			ss[key] = ToInt(node)
		}
		return ss
	}
	return nil
}

func ToBoolSlice(object interface{}) []bool {
	if object == nil {
		return nil
	}
	var ss []bool
	switch v := object.(type) {
	case []bool:
		return v
	case []string:
		ss = make([]bool, len(v))
		for key, node := range v {
			ss[key] = ToBool(node)
		}
		return ss
	case []interface{}:
		ss = make([]bool, len(v))
		for key, node := range v {
			ss[key] = ToBool(node)
		}
		return ss
	}
	return nil
}

func ToFloatSlice(object interface{}) []float32 {
	if object == nil {
		return nil
	}
	var ss []float32
	switch v := object.(type) {
	case []float32:
		return v
	case []interface{}:
		ss = make([]float32, len(v))
		for key, node := range v {
			ss[key] = ToFloat32(node)
		}
		return ss
	}
	return nil
}

func ToBytesSlice(object interface{}) [][]byte {
	if object == nil {
		return nil
	}
	//fmt.Println("tobytes", object)
	var ss [][]byte
	switch v := object.(type) {
	case [][]byte:
		//fmt.Println("tobytes 1", v)
		return v
	case []interface{}:
		//fmt.Println("tobytes 2", v)
		ss = make([][]byte, len(v))
		for key, node := range v {
			ss[key] = ToBytes(node)
		}
		return ss
	case []string:
		//fmt.Println("tobytes 3", v)
		ss = make([][]byte, len(v))
		for key, node := range v {
			ss[key] = ToBytes(node)
		}
		return ss
	}
	return nil
}

func ToType(source interface{}, targetType reflect.Type) interface{} {
	var err error
	if source == nil {
		return nil
	}
	v := reflect.ValueOf(source)
	if !v.IsValid() {
		return nil
	}
	t := v.Type()
	if t.ConvertibleTo(targetType) {
		a := v.Convert(targetType)
		return a.Interface()
	}
	b, err := json.Marshal(source)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	tt := reflect.New(targetType)
	ti := tt.Interface()
	err = json.Unmarshal(b, ti)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return tt.Elem().Interface()
}

func Copy(object interface{}) interface{} {
	if object == nil {
		return ""
	}
	switch v := object.(type) {
	case []byte:
		vv := make([]byte, len(v))
		copy(vv, v)
		return vv
	}
	return object
}

func str2bytes(s string) []byte {
	x := (*[2]uintptr)(unsafe.Pointer(&s))
	h := [3]uintptr{x[0], x[1], x[1]}
	return *(*[]byte)(unsafe.Pointer(&h))
}

func bytes2str(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func int642bytes(i int64) []byte {
	var buf = make([]byte, 8)
	binary.LittleEndian.PutUint64(buf, uint64(i))
	return buf
}
func int162bytes(i int16) []byte {
	var buf = make([]byte, 2)
	binary.LittleEndian.PutUint16(buf, uint16(i))
	return buf
}

func uint642bytes(i uint64) []byte {
	var buf = make([]byte, 8)
	binary.LittleEndian.PutUint64(buf, i)
	return buf
}
func uint162bytes(i uint16) []byte {
	var buf = make([]byte, 2)
	binary.LittleEndian.PutUint16(buf, i)
	return buf
}

//func bytes2int64(buf []byte) int64 {
//	return int64(binary.LittleEndian.Uint64(buf))
//}

func float322bytes(float float32) []byte {
	bits := math.Float32bits(float)
	bytes := make([]byte, 4)
	binary.LittleEndian.PutUint32(bytes, bits)

	return bytes
}

func bytes2float32(bytes []byte) float32 {
	bits := binary.LittleEndian.Uint32(bytes)

	return math.Float32frombits(bits)
}
